package org.sean.framework.web.config;

import org.sean.framework.code.StatusInfo;
import org.sean.framework.exception.BaseRuntimeException;
import org.sean.framework.exception.StatusException;
import org.sean.framework.logging.Logger;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ResponseStatus;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 异常处理器
 *
 * @author xielei
 */
public class ThrowableHandler {
    private static final Logger LOGGER = Logger.newInstance(ThrowableHandler.class);

    private static final Map<Class<?>, StatusInfo> EXCEPTIONS = new HashMap<>();

    static {
        // 请求参数异常
        EXCEPTIONS.put(NumberFormatException.class, StatusInfo.paramsInvalidError());
        EXCEPTIONS.put(HttpMessageNotReadableException.class, StatusInfo.paramsInvalidError());
        EXCEPTIONS.put(MethodArgumentNotValidException.class, StatusInfo.paramsInvalidError());
        // 数据异常
        EXCEPTIONS.put(SQLException.class, StatusInfo.databaseAccessFailed());
        try {
            EXCEPTIONS.put(DataAccessException.class, StatusInfo.dataAccessFailed());
        } catch (Exception e) {
            // do nothing
        }
    }

    private ThrowableHandler() {
    }

    /**
     * 取异常信息
     *
     * @param e 异常
     * @return StatusInfo
     * @throws Throwable 异常
     */
    public static StatusInfo handle(Throwable e) throws Throwable {
        LOGGER.printErrorStackTrace(e);
        // 定义状态码,直接返回
        if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null) {
            throw e;
        }

        Throwable oom = BaseRuntimeException.findThrowable(e, OutOfMemoryError.class);
        // 内存溢出 程序关闭
        if (oom != null) {
            LOGGER.error("OutOfMemoryError:", e);
            System.exit(0);
            throw e;
        }

        StatusException se = StatusException.findStatusException(e);
        StatusInfo statusInfo;
        // 已知异常
        if (se != null) {
            statusInfo = se.getStatusInfo();
        } else {
            // 未知异常
            statusInfo = EXCEPTIONS.entrySet().stream()
                    .filter(entry -> entry.getKey().isInstance(e))
                    .map(Map.Entry::getValue).findFirst()
                    .orElse(StatusInfo.unknownError().setAnyMessage(e.getMessage()));
        }

        return statusInfo;
    }

}
